﻿#region

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;

#endregion

namespace RGG.sharp
{
    public delegate void GbDelegate(object sender, GbEventArgs e);

    public class GbEventArgs : EventArgs
    {
    }

    /// <summary>
    ///     各种几何体形状需要实现的基类
    /// </summary>
    public class GeometricBody
    {
        /// <summary>
        /// </summary>
        /// <param name="configItem"></param>
        /// <param name="largeDotMatrix">
        ///     需要被绘图的点阵，只需要第一次调用时传值即可
        ///     维度分别为LargeWidth，LargeHeight，2的矩阵
        ///     前两位为点的坐标，后一位为点存在与颜色值
        ///     即LargeDotMatrix[width,height,0] = 0表示点不存在，1表示存在
        ///     LargeDotMatrix[width,height,1] = colorValue;
        /// </param>
        /// <param name="drawLeft">默认值为largeDotMatrix.GetLength(1)/2-1</param>
        /// <param name="drawTop">默认值为0，纵轴方向为从上到下为正方向</param>
        public GeometricBody(GeometricBodyConfigItem configItem, int[,,] largeDotMatrix = null, int drawLeft = -1,
            int drawTop = 0)
        {
            _isDrawed = false;
            Width = configItem.Width;
            Height = configItem.Height;
            ColorGB = configItem.ColorGB;
            DotMatrix = configItem.DotMatrix.ToList();
            if (LargeDotMatrix == null)
            {
                Debug.Assert(largeDotMatrix != null, "最少需要提供一次三维数组的值！");
            }
            LargeDotMatrix = largeDotMatrix;
            Debug.Assert(LargeDotMatrix != null, "LargeDotMatrix != null");
            DrawLeft = drawLeft != 0 ? drawLeft : 0;//TODO:默认值：LargeDotMatrix.GetLength(0)/ 2- 1;
            DrawTop = drawTop;
            Debug.Assert(DotMatrix.Count == Height*Width, "要求点阵的长度为与Height的乘积");
        }

        /// <summary>
        ///     对于按下某一个按键需要实现的功能
        ///     当不可以继续下降时，返回false
        /// </summary>
        /// <param name="keyDown"></param>
        /// <returns>返回false表示方块触碰到了其他方块(下方)</returns>
        public bool KeyDown(KeyDownDirection keyDown)
        {
            if (BeforeKeyDown != null) BeforeKeyDown.BeginInvoke(this, new GbEventArgs(), null, null);
            try
            {
                switch (keyDown)
                {
                    case KeyDownDirection.Down:
                        return MoveDown();
                    case KeyDownDirection.Right:
                        return MoveRight();
                    case KeyDownDirection.Up:
                        return Rotate();
                    case KeyDownDirection.Left:
                        return MoveLeft();
                    default:
                        throw new ArgumentException("KeyDownDirection不包含枚举值：" + keyDown);
                }
            }
            finally
            {
                if (AfterKeyDown != null) AfterKeyDown.BeginInvoke(this, new GbEventArgs(), null, null);
            }
        }

        /// <summary>
        ///     将本几何体画到外面的点阵中
        /// </summary>
        private void Redraw()
        {
            if (BeforeRedraw != null)
            {
                BeforeRedraw.BeginInvoke(this, new GbEventArgs(), null, null);
            }

            _isDrawed = true;
            for (var i = 0; i < Height; ++i)
            {
                int y = DrawTop + i;
                if (y < 0) continue;
                for (var j = 0; j < Width; ++j)
                {
                    int x = DrawLeft + j;
                    if (DotMatrix[i * Width + j] != 0)
                    {
                        LargeDotMatrix[x, y, 0] =1;
                        LargeDotMatrix[x, y, 1] = ColorGB.ToArgb();
                    }
                }
            }

            if (AfterRedraw != null)
            {
                AfterRedraw.BeginInvoke(this, new GbEventArgs(), null, null);
            }
        }

        /// <summary>
        ///     清空在大的点阵上画的点
        /// </summary>
        private void Clear()
        {
            if (BeforeClear != null) BeforeClear.BeginInvoke(this, new GbEventArgs(), null, null);

            #region 记录调用的堆栈

            /*
#if DEBUG
            string filename = DebugConfig.DebugTextPath + "DebugStackTraceGBClear.txt";
            using (FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write))
            {
                StackTrace st = new StackTrace();
                StackFrame[] sfs = st.GetFrames();
                for (int u = 0; u < sfs.Length; ++u)
                {
                    MethodBase mb = sfs[u].GetMethod();
                    byte[] buffer =
                        Encoding.Default.GetBytes(string.Format("[CALL STACK][{0}]: {1}.{2}\n", u,
                            mb.DeclaringType.FullName, mb.Name));
                    fs.Write(buffer,0,buffer.Length);
                }
            }

#endif
            */

            #endregion

            if (_isDrawed)
            {
                _isDrawed = false;
                for (var i = 0; i < Height; ++i)
                {
                    int y = DrawTop +  i;
                    if (y < 0) continue;
                    for (var j = 0; j < Width; ++j)
                    {
                        int x = DrawLeft + j;
                        if (DotMatrix[i*Width + j] != 0)
                        {
                            Debug.Assert(LargeDotMatrix[x, y, 0] != 0);
                            LargeDotMatrix[x, y, 0] = 0;
                            LargeDotMatrix[x, y, 1] = 0;
                        }
                    }
                }
            }

            if (AfterClear != null) AfterClear.BeginInvoke(this, new GbEventArgs(), null, null);
        }

        /// <summary>
        ///     Reverse Direction
        /// </summary>
        /// <returns>return false if it will touch the bottom or other cell</returns>
        public bool Rotate()
        {
            if (BeforeRotate != null) BeforeRotate.BeginInvoke(this, new GbEventArgs(), null, null);

            int temp;
            if (_isDrawed) Clear();
            switch (Width*Height)
            {
                case 4:
                    //不是田字格，田字格不需要发生变化
                    if (Width != 2)
                    {
                        if (Width > Height)
                        {
                            DrawLeft -= 2;
                            DrawTop += 2;
                        }
                        else
                        {
                            DrawLeft += 2;
                            DrawTop -= 2;
                        }
                        if (DrawLeft < 0)
                        {
                            DrawLeft = 0;
                        }
                        else if (DrawLeft + Width >= LargeDotMatrix.GetLength(1))
                        {
                            DrawLeft = LargeDotMatrix.GetLength(1) - Width - 1;
                        }
                        temp = Width;
                        Width = Height;
                        Height = temp;
                        if (CanDown())
                        {
                            return false;
                        }
                    }
                    break;
                case 6:
                    var canLeft = CanLeft();
                    var canRight = CanRight();
                    if (!canLeft && !canRight)
                    {
//不可以转动方向
                        return true;
                    }

                    //        .._
                    //... =>  .._
                    //...     .._
                    //TODO:认为刚才从上面掉下来，可以转动，可能会有问题。
                    if (Width == 3)
                    {
                        DrawTop -= 1;
                        Width = 2;
                        Height = 3;
                    }
                    //  ..      
                    //  ..  =>  ..._ Or _...
                    //  ..      ..._    _...   
                    else
                    {
                        Width = 3;
                        Height = 2;
                        DrawTop += 1;
                        if (canRight)
                        {
                        }
                        else
                        {
                            DrawLeft -= 1;
                        }
                    }
                    break;
            }
            Redraw();

            if (AfterRotate != null) AfterRotate.BeginInvoke(this, new GbEventArgs(), null, null);
            return true;
        }

        /// <summary>
        ///     Move To the right side for one step
        /// </summary>
        /// <returns>must return true;</returns>
        protected bool MoveRight()
        {
            if (CanRight())
            {
                DrawLeft += 1;
                Redraw();
            }
            return true;
        }

        /// <summary>
        ///     Move To the bottom direction for one step
        /// </summary>
        /// <returns>return false if touch the bottom</returns>
        protected bool MoveDown()
        {
            var result = CanDown();
            if (result)
            {
                DrawTop += 1;
                Redraw();

                #region Debug

                /*
#if DEBUG
                var filename = DebugConfig.DebugTextPath + "DebugGBMoveDown.txt";
                using (
                    var fs = new FileStream(filename,
                        FileMode.Append, FileAccess.Write))
                {
                    for (var i = 0; i < LargeDotMatrix.GetLength(0); ++i)
                    {
                        for (var j = 0; j < LargeDotMatrix.GetLength(1); ++j)
                        {
                            if (LargeDotMatrix[i, j, 0] != 0)
                            {
                                var buffer =
                                    Encoding.Default.GetBytes(string.Format("{0},{1},{2}\n", i, j,
                                        Color.FromArgb(LargeDotMatrix[i, j, 1])));
                                fs.Write(buffer, 0, buffer.Length);
                            }
                        }
                    }
                }
#endif
                */

                #endregion
            }
            return result;
        }

        /// <summary>
        ///     Move To the left side for one step
        /// </summary>
        /// <returns>must return true;</returns>
        protected bool MoveLeft()
        {
            if (CanLeft())
            {
                DrawLeft -= 1;
                Redraw();
            }
            return true;
        }

        /// <summary>
        ///     检测当前状态是否即将碰到其他方块
        /// </summary>
        /// <returns>return false if</returns>
        private bool CanDown()
        {
            if (DrawTop + Height >= LargeDotMatrix.GetLength(1))
            {
                return false;
            }
            var result = true;
            for (var i = 0; i < Width; ++i)
            {
                for (var j = Height - 1; j >= 0; --j)
                {
                    var x = DrawLeft + i;
                    var y = DrawTop + j+1;
                    //当这个点存在而且这个点的下一个点不存在时才需要判断
                    if (DotMatrix[j*Width + i] != 0 &&
                        ((j + 1)*Width + i >= DotMatrix.Count || DotMatrix[(j + 1)*Width + i] == 0))
                    {
                        if (LargeDotMatrix[x, y, 0] != 0)
                        {
                            #region Debug

#if DEBUG
                            var filename = DebugConfig.DebugTextPath + "DebugGBCanDown.txt";
                            using (var fs = new FileStream(filename, FileMode.Append, FileAccess.Write))
                            {
                                byte[] outBuffer =
                                    Encoding.Default.GetBytes(string.Format("位置为{0},{1}的{2},{3}相邻{4},{5}\r\n",
                                        DrawLeft,
                                        DrawTop, i, j, x, y));
                                fs.Write(outBuffer, 0, outBuffer.Length);
                            }
#endif

                            #endregion

                            result = false;
                        }
                        break;
                    }
                }
                if (!result) break;
            }
            return result;
        }

        /// <summary>
        ///     确定在当前状态下是否还可以向左移动
        ///     如果可以移动,原来的点将不存在了。
        /// </summary>
        /// <returns></returns>
        private bool CanLeft()
        {
            if (DrawLeft <= 0)
            {
                return false;
            }
            var result = true;
            for (var i = 0; i < Height; ++i)
            {
                var y = DrawTop + i;
                if (y < 0) continue;
                for (var j = 0; j < Width; ++j)
                {
                    var x = DrawLeft + j-1;
                    if (DotMatrix[i*Width + j] != 0)
                    {
                        if (LargeDotMatrix[x, y, 0] != 0)
                        {
                            result = false;
                        }
                        break;
                    }
                }
                if (!result) break;
            }
            return result;
        }

        /// <summary>
        ///     确定在当前状态下是否还可以向右移动,
        /// </summary>
        /// <returns></returns>
        private bool CanRight()
        {
            if (DrawLeft+Width >= LargeDotMatrix.GetLength(0))
            {
                return false;
            }
            var result = true;
            for (var i = 0; i < Height; ++i)
            { 
                var y = DrawTop + i;
                if (y < 0) continue;
                for (var j = Width-1; j >= 0; --j)
                {
                    var x = DrawLeft + j + 1;
                    if (DotMatrix[(i + 1)*Width - 1] != 0)
                    {
                        if (LargeDotMatrix[x, y, 0] != 0)
                        {
                            result = false;
                        } 
                        break;
                    }
                }
                if (!result) break;
            }
            return true;
        }

        #region Events

        /// <summary>
        ///     调用Redraw函数之前执行
        /// </summary>
        public event GbDelegate BeforeRedraw;

        /// <summary>
        ///     调用Redraw函数之后执行
        /// </summary>
        public event GbDelegate AfterRedraw;

        /// <summary>
        ///     调用Clear函数之前执行
        /// </summary>
        public event GbDelegate BeforeClear;

        /// <summary>
        ///     调用Clear函数之后执行
        /// </summary>
        public event GbDelegate AfterClear;

        /// <summary>
        ///     调用Rotate函数之前执行
        /// </summary>
        public event GbDelegate BeforeRotate;

        /// <summary>
        ///     调用Rotate函数之后执行
        /// </summary>
        public event GbDelegate AfterRotate;

        /// <summary>
        ///     调用KeyDown函数之前执行
        /// </summary>
        public event GbDelegate BeforeKeyDown;

        /// <summary>
        ///     调用KeyDown函数之后执行
        /// </summary>
        public event GbDelegate AfterKeyDown;

        #endregion

        #region Field

        /// <summary>
        ///     几何体的颜色
        /// </summary>
        private Color _colorGb;

        /// <summary>
        ///     记录该几何体的Width宽，Height高的点阵
        /// </summary>
        private List<int> _dotMatrix;

        /// <summary>
        ///     绘图时的左方开始的坐标
        /// </summary>
        private int _drawLeft;

        /// <summary>
        ///     绘图时的上方开始的坐标
        /// </summary>
        private int _drawTop;

        /// <summary>
        ///     几何体形状的高度
        /// </summary>
        private int _height;

        /// <summary>
        ///     需要被绘图的点阵
        /// </summary>
        private static int[,,] _largeDotMatrix;

        /// <summary>
        ///     几何体形状的宽度
        /// </summary>
        private int _width;

        private bool _isDrawed;

        #endregion

        #region Attribute

        /// <summary>
        ///     几何体形状的宽度
        /// </summary>
        public int Width
        {
            get { return _width; }
            private set { _width = value; }
        }

        /// <summary>
        ///     几何体形状的高度
        /// </summary>
        public int Height
        {
            get { return _height; }
            private set { _height = value; }
        }

        /// <summary>
        ///     几何体的颜色
        /// </summary>
        public Color ColorGB
        {
            get { return _colorGb; }
            set { _colorGb = value; }
        }

        /// <summary>
        ///     Width宽，Height高的点阵
        /// </summary>
        public List<int> DotMatrix
        {
            get { return _dotMatrix; }
            protected set { _dotMatrix = value; }
        }

        /// <summary>
        ///     需要被绘图的点阵，维度分别为LargeWidth，LargeHeight，2的矩阵
        ///     前两位为点的坐标，后一位为点存在与颜色值
        ///     即LargeDotMatrix[width,height,0] = 0表示点不存在，1表示存在
        ///     LargeDotMatrix[width,height,1] = colorValue;
        /// </summary>
        public int[,,] LargeDotMatrix
        {
            get { return _largeDotMatrix; }
            set { _largeDotMatrix = value; }
        }

        /// <summary>
        ///     绘图时的左方开始的坐标
        ///     更改时将会删除掉原来所占用的点，但是不会重画，需要手工调用Redraw方法
        /// </summary>
        public int DrawLeft
        {
            get { return _drawLeft; }
            private set
            {
                if (_isDrawed) Debug.Assert(value + Width <= LargeDotMatrix.GetLength(0));
                Clear();
                _drawLeft = value < 0 ? 0 : value;
            }
        }

        /// <summary>
        ///     绘图时的上方开始的坐标
        ///     更改时将会删除掉原来所占用的点，但是不会重画，需要手工调用Redraw方法
        /// </summary>
        public int DrawTop
        {
            get { return _drawTop; }
            private set
            {
                if (_isDrawed) Debug.Assert(value + Height <= LargeDotMatrix.GetLength(1));
                Clear();
                _drawTop = value < -1 ? -1 : value;
            }
        }

        #endregion
    }


    public enum KeyDownDirection
    {
        Up,
        Right,
        Down,
        Left
    };
}